package com.core.netty.http;

import com.core.comment.StringUtil;
import com.core.netty.http.Intercept.MyIntercept;
import com.core.netty.http.Intercept.MyInterceptFactory;
import com.core.netty.http.work.*;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.util.Date;
import java.util.Map;


/**
 * http 请求处理器
 * 2018/11/1.
 */

@ChannelHandler.Sharable
public class HttpRequestHandler extends SimpleChannelInboundHandler<HttpObject> {
    private static final HttpDataFactory factory = new DefaultHttpDataFactory(true);

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject hob) throws Exception {


        // HTTP客户端程序有一个实体的主体部分要发送给服务器，但希望在发送之前查看下服务器是否会
        // 接受这个实体，所以在发送实体之前先发送了一个携带100 Continue的Expect请求首部的请求。
        // 服务器在收到这样的请求后，应该用 100 Continue或一条错误码来进行响应。
        // Laruence的博客写到：在使用curl做POST的时候, 当要POST的数据大于1024字节的时候, curl并不会直接就发起POST请求, 而是会分为俩步,
        // 1. 发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
        // 2. 接收到Server返回的100-continue应答以后, 才把数据POST给Server

        long start = new Date().getTime();

        if (hob instanceof HttpRequest) {
            handleRequest(ctx, hob);
//            HttpHeaders headers = ((HttpRequest) hob).headers();
//            if (headers.get("content-type") != null && headers.get("content-type").contains("multipart/form-data")) {
//               handleFile(ctx, hob);
//            } else {
//                handleRequest(ctx, hob);
//            }
        }


        System.out.println((new Date().getTime() - start) / 1000 + "ms");

    }

    /**
     * 处理请求
     *
     * @param ctx
     * @param hob
     * @return
     * @throws Exception
     */
    private HttpMyReponse handleRequest(ChannelHandlerContext ctx, HttpObject hob) throws Exception {
        FullHttpRequest req = (FullHttpRequest) hob;
        HttpMyRequest myreq = new HttpMyRequest();
        HttpMyReponse myrep = new HttpMyReponse();
        //处理请求头
        HttpHeaders headers = req.headers();
        handleHeaders(headers, myreq, myrep);
        //回复对象填充ctx
        myrep.setCtx(ctx);
        //取请求参数
        Map<String, String> parmMap = new RequestParser(req).parse(); // 将GET, POST所有请求参数转换成Map对象
        myreq.initPars(parmMap);
        //取请求的ip和端口和请求方法
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = insocket.getAddress().getHostAddress();
        int port = insocket.getPort();


        //填充request
        myreq.setRemoteHost(ip);
        myreq.setPort(port);
        myreq.setMethod(req.method().name());


        //取请求url
        String uri = req.uri();
        uri = uri.replaceAll("/+", "/");
        myreq.setUrl(uri);

        //取url相应的拦截器并执行拦截操作
        MyInterceptFactory.doIntercept(uri, myreq, myrep);
        //取url相应的serlvet 填充并执行
        String msg;
        HttpMySerlvet mySer = getSerlvet(uri);
        if (mySer != null) {

            //执行请求的执行方法
            mySer.domainBefor(myreq, myrep);
            mySer.domain(myreq, myrep);
            mySer.domainAfter();
            //取出回应的字符串
            msg = mySer.getMyrep().getMsg();

        } else {
            myrep.fullWrite("二百五，URL不对！");
        }
        return myrep;


    }

    /**
     * 处理请求头 处理 cookie 和 session
     *
     * @param headers
     */
    private void handleHeaders(HttpHeaders headers, HttpMyRequest req, HttpMyReponse rep) {
        req.setHeaders(headers);

        String cookie = headers.get("Cookie");
        String sessionid = null;
        //取cookie里的sessid 把cookie填充到request里
        try {
            if (StringUtil.isNotBlack(cookie)) {
                String[] cookies = cookie.split(";");
                for (String c : cookies) {
                    if (!"HttpOnly".equals(c) && c.contains("=")) {
                        String[] cs = c.split("=");
                        req.setCookie(cs[0] != null ? cs[0].trim() : null, cs[1] != null ? cs[1].trim() : null);
                        if (cs[0] != null && "mysessionid".equals(cs[0].trim())) {
                            sessionid = cs[1] != null ? cs[1].trim() : null;

                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //生成session 或 取sesssion
        if (SessionUtil.get(sessionid) == null) {
            sessionid = StringUtil.getUuid();
            HttpMySession se = new HttpMySession();
            se.setSessionId(sessionid);
            SessionUtil.put(sessionid, se);
            req.setSession(se);
            rep.setHeader("Set-Cookie", "mysessionid=" + sessionid + "; Path=/; HttpOnly");
        } else {
            HttpMySession session = SessionUtil.get(sessionid);
            req.setSession(session);
        }
    }


    /**
     * 获取serlvet
     *
     * @param uri 请求路径
     * @return
     */
    private HttpMySerlvet getSerlvet(String uri) {
        HttpMySerlvet mySer = null;
        if (SerlvetUtil.get(uri) != null) {
            mySer = (HttpMySerlvet) SerlvetUtil.get(uri);
        } else {
            for (Map.Entry<String, MySerlvet> entry : SerlvetUtil.getMap().entrySet()) {
                String key = entry.getKey();
                if (key.contains("*")) {
                    if (key.endsWith("*") && uri.contains(key.substring(0, key.indexOf("*")))) {
                        mySer = (HttpMySerlvet) entry.getValue();
                    }

                }

            }
        }
        return mySer;
    }

    private void handleFile(ChannelHandlerContext cxt, HttpObject hob) throws Exception {
        HttpPostRequestDecoder httpDecoder = new HttpPostRequestDecoder(factory, (HttpRequest) hob);
        if (hob instanceof LastHttpContent) {
            System.out.println("lastHttpContent");
            writeChunk(cxt, httpDecoder);
            httpDecoder.destroy();
            httpDecoder = null;
            System.out.println("ddd");
            new HttpMyReponse(cxt).fullWrite("成功");
            // handleRequest(cxt, hob);
        }
    }

    private void writeChunk(ChannelHandlerContext cxt, HttpPostRequestDecoder httpDecoder) throws IOException {
        while (httpDecoder.hasNext()) {
            InterfaceHttpData data = httpDecoder.next();
            if (data != null && InterfaceHttpData.HttpDataType.FileUpload.equals(data.getHttpDataType())) {
                final FileUpload fileUpload = (FileUpload) data;
                final File file = new File("F:/tv/" + fileUpload.getFilename());

                try (FileChannel inputChannel = new FileInputStream(fileUpload.getFile()).getChannel();
                     FileChannel outputChannel = new FileOutputStream(file).getChannel()) {
                    outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
                    //  ResponseUtil.response(ctx, request, new GeneralResponse(HttpResponseStatus.OK, "SUCCESS", null));
                }
            }
        }
    }


}